项目结构概述#
良好的项目结构是开发高质量 Skills 的基础。本节将详细介绍 Skills 项目的标准结构和最佳实践。
1. 基础结构#
bashmy-skill/ ├── src/ # 源代码目录 │ └── skills/ # Skills 模块 │ ├── __init__.py # 模块初始化 │ ├── my_skill.py # 主 Skill 实现 │ ├── utils/ # 工具函数 │ │ ├── __init__.py │ │ ├── helpers.py # 辅助函数 │ │ └── validators.py # 验证器 │ └── models/ # 数据模型 │ ├── __init__.py │ └── schemas.py # 数据模式 ├── tests/ # 测试目录 │ ├── __init__.py │ ├── conftest.py # pytest 配置 │ ├── test_my_skill.py # Skill 测试 │ ├── test_utils.py # 工具测试 │ └── fixtures/ # 测试固件 │ ├── __init__.py │ └── data/ # 测试数据 ├── docs/ # 文档目录 │ ├── api.md # API 文档 │ ├── usage.md # 使用文档 │ └── development.md # 开发文档 ├── examples/ # 示例目录 │ ├── basic_usage.py # 基本使用示例 │ └── advanced_usage.py # 高级使用示例 ├── scripts/ # 脚本目录 │ ├── build.py # 构建脚本 │ ├── deploy.py # 部署脚本 │ └── test.sh # 测试脚本 ├── configs/ # 配置目录 │ ├── development.yaml # 开发配置 │ ├── production.yaml # 生产配置 │ └── testing.yaml # 测试配置 ├── resources/ # 资源目录 │ ├── templates/ # 模板文件 │ ├── static/ # 静态文件 │ └── data/ # 数据文件 ├── .gitignore # Git 忽略文件 ├── .pylintrc # Pylint 配置 ├── .mypy.ini # MyPy 配置 ├── pyproject.toml # 项目配置 ├── setup.py # 安装脚本 ├── requirements.txt # 依赖列表 ├── requirements-dev.txt # 开发依赖 ├── README.md # 项目说明 ├── LICENSE # 许可证 └── CHANGELOG.md # 变更日志 ~~~### 2. 详细说明 #### 2.1 源代码目录(src/) # src/skills/__init__.py """ Skills 模块 """ from skills.my_skill import MySkill __all__ = ["MySkill"] __version__ = "0.1.0" ```python # src/skills/my_skill.py """ 主 Skill 实现 """ from typing import Dict, Any from claude_code_sdk import Skill, SkillContext, SkillResult class MySkill(Skill): """自定义 Skill 实现""" def __init__(self): super().__init__( name="my-skill", version="0.1.0", description="A custom Claude Code skill" ) def get_parameters_schema(self) -> Dict[str, Any]: """获取参数模式""" return { "type": "object", "properties": { "input": { "type": "string", "description": "Input text to process" } }, "required": ["input"] } def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult: """执行 Skill""" input_text = parameters["input"] output = self.process_input(input_text) return SkillResult( success=True, data={ "input": input_text, "output": output } ) def process_input(self, input_text: str) -> str: """处理输入文本""" return input_text.upper()
python# src/skills/utils/__init__.py """ 工具函数模块 """ from skills.utils.helpers import format_output, validate_input from skills.utils.validators import validate_parameters __all__ = ["format_output", "validate_input", "validate_parameters"]
python# src/skills/utils/helpers.py """ 辅助函数 """ from typing import Any def format_output(data: Any, format_type: str = "json") -> str: """格式化输出""" if format_type == "json": import json return json.dumps(data, indent=2) elif format_type == "yaml": import yaml return yaml.dump(data) else: return str(data) def validate_input(input_text: str) -> bool: """验证输入""" return isinstance(input_text, str) and len(input_text) > 0
python# src/skills/utils/validators.py """ 验证器 """ from typing import Dict, Any def validate_parameters(parameters: Dict[str, Any], schema: Dict[str, Any]) -> bool: """验证参数""" required = schema.get("required", []) properties = schema.get("properties", {}) for param in required: if param not in parameters: return False param_schema = properties.get(param, {}) param_type = param_schema.get("type") if param_type: if param_type == "string" and not isinstance(parameters[param], str): return False elif param_type == "number" and not isinstance(parameters[param], (int, float)): return False elif param_type == "boolean" and not isinstance(parameters[param], bool): return False return True
python# src/skills/models/__init__.py """ 数据模型模块 """ from skills.models.schemas import InputSchema, OutputSchema __all__ = ["InputSchema", "OutputSchema"]
python# src/skills/models/schemas.py """ 数据模式 """ from dataclasses import dataclass from typing import Optional @dataclass class InputSchema: """输入模式""" input: str options: Optional[dict] = None @dataclass class OutputSchema: """输出模式""" success: bool data: dict message: Optional[str] = None
2.2 测试目录(tests/)
python# tests/__init__.py """ 测试模块 """
python# tests/conftest.py """ pytest 配置 """ import pytest from claude_code_sdk import SkillContext @pytest.fixture
def skill_context(): """Skill 上下文固件""" return SkillContext()
@pytest.fixture def sample_parameters(): """示例参数固件""" return { "input": "test input" }
@pytest.fixture def sample_context(): """示例上下文固件""" return { "project": { "name": "test-project", "path": "/tmp/test-project" }, "user": { "id": "user123", "name": "Test User" } }
python# tests/test_my_skill.py """ Skill 测试 """ import pytest from skills.my_skill import MySkill class TestMySkill: """MySkill 测试类""" def setup_method(self): """设置方法""" self.skill = MySkill() def test_skill_initialization(self): """测试 Skill 初始化""" assert self.skill.name == "my-skill" assert self.skill.version == "0.1.0" assert self.skill.description == "A custom Claude Code skill" def test_get_parameters_schema(self): """测试获取参数模式""" schema = self.skill.get_parameters_schema() assert "properties" in schema assert "input" in schema["properties"] assert "required" in schema assert "input" in schema["required"] def test_execute_success(self, skill_context): """测试成功执行""" parameters = {"input": "hello"} result = self.skill.execute(parameters, skill_context) assert result.success assert result.data["output"] == "HELLO" def test_execute_missing_parameter(self, skill_context): """测试缺少参数""" parameters = {} with pytest.raises(KeyError): self.skill.execute(parameters, skill_context) def test_process_input(self): """测试处理输入""" assert self.skill.process_input("hello") == "HELLO" assert self.skill.process_input("world") == "WORLD" assert self.skill.process_input("123") == "123" ```python # tests/test_utils.py """ 工具函数测试 """ import pytest from skills.utils.helpers import format_output, validate_input from skills.utils.validators import validate_parameters class TestHelpers: """辅助函数测试""" def test_format_output_json(self): """测试 JSON 格式化""" data = {"key": "value"} result = format_output(data, "json") assert '"key": "value"' in result def test_format_output_yaml(self): """测试 YAML 格式化""" data = {"key": "value"} result = format_output(data, "yaml") assert "key: value" in result def test_validate_input_valid(self): """测试有效输入""" assert validate_input("hello") == True assert validate_input("world") == True def test_validate_input_invalid(self): """测试无效输入""" assert validate_input("") == False assert validate_input(123) == False class TestValidators: """验证器测试""" def test_validate_parameters_valid(self): """测试有效参数""" parameters = {"input": "test"} schema = { "properties": { "input": {"type": "string"} }, "required": ["input"] } assert validate_parameters(parameters, schema) == True def test_validate_parameters_missing_required(self): """测试缺少必需参数""" parameters = {} schema = { "properties": { "input": {"type": "string"} }, "required": ["input"] } assert validate_parameters(parameters, schema) == False def test_validate_parameters_wrong_type(self): """测试错误类型""" parameters = {"input": 123} schema = { "properties": { "input": {"type": "string"} }, "required": ["input"] } assert validate_parameters(parameters, schema) == False
2.3 配置文件
yaml```yaml # configs/development.yaml # 开发环境配置 version: "1.0" skills: my-skill: enabled: true debug: true log_level: DEBUG logging: level: DEBUG format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" output: console cache: enabled: true ttl: 3600 storage: memory ```> # configs/production.yaml # 生产环境配置 version: "1.0" skills: my-skill: enabled: true debug: false log_level: INFO logging: level: INFO format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" output: file file_path: /var/log/my-skill.log cache: enabled: true ttl: 7200 storage: redis redis_host: localhost redis_port: 6379 ``` ~~~yaml ```yaml ``` # configs/testing.yaml # 测试环境配置 version: "1.0" skills: my-skill: enabled: true debug: true log_level: DEBUG logging: level: DEBUG format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" output: console cache: enabled: false testing: mock_external_calls: true use_test_data: true ```## 项目组织原则 ### 1. 模块化设计 ## 模块化原则 ### 单一职责 每个模块只负责一个功能 - my_skill.py: 主 Skill 逻辑 - helpers.py: 辅助函数 - validators.py: 验证逻辑 ### 高内聚低耦合 模块内部紧密相关,模块之间松散耦合 - 使用接口定义 - 依赖注入 - 事件驱动 ### 可复用性 设计可复用的组件 - 工具函数独立 - 数据模型通用 - 配置灵活 ``` ### 2. 命名规范 ~~~markdown ```markdown ## 命名规范 ### 文件命名 - 使用小写字母和下划线 - 描述性名称 - 避免缩写 示例: - my_skill.py ✓ - helpers.py ✓ - validators.py ✓ - MySkill.py ✗ - helper.py ✗ ### 类命名 - 使用大驼峰命名法 - 名词或名词短语 - 描述性名称 示例: - MySkill ✓ - SkillContext ✓ - InputSchema ✓ - mySkill ✗ - skill_context ✗ ### 函数命名 - 使用小写字母和下划线 - 动词或动词短语 - 描述性名称 示例: - execute() ✓ - process_input() ✓ - validate_parameters() ✓ - Execute() ✗ - processInput() ✗ ### 变量命名 - 使用小写字母和下划线 - 描述性名称 - 避免单字母变量 示例: - input_text ✓ - result_data ✓ - skill_context ✓ - txt ✗ - data ✗ ```### 3. 目录组织 ## 目录组织原则 ### 按功能组织 将相关功能放在同一目录 - utils/: 工具函数 - models/: 数据模型 - tests/: 测试代码 ### 按层次组织 保持清晰的层次结构 - src/: 源代码 - tests/: 测试代码 - docs/: 文档 ### 避免过深嵌套 目录层次不超过 3 层 - src/skills/my_skill.py ✓ - src/skills/utils/helpers.py ✓ - src/skills/utils/data/processors/helpers.py ✗ ``` ## 文档组织 ### 1. API 文档 ~~~markdown ```markdown # docs/api.md # MySkill API 文档 ## 概述 MySkill 是一个自定义的 Claude Code Skill,用于处理文本输入。 ## 参数 ### input (必需) - 类型: string - 描述: 要处理的输入文本 - 示例: "hello world" ### options (可选) - 类型: object - 描述: 附加选项 - 属性: - format: 输出格式 (json, yaml, text) - case: 大小写转换 (upper, lower, title) ## 返回值 ### success - 类型: boolean - 描述: 操作是否成功 ### data - 类型: object - 描述: 返回的数据 - 属性: - input: 原始输入 - output: 处理后的输出 ### message - 类型: string (可选) - 描述: 附加消息 ## 示例 ### 基本使用 ```python ```python from skills.my_skill import MySkill skill = MySkill() result = skill.execute( {"input": "hello world"}, context ) print(result.data["output"]) # "HELLO WORLD" ### 高级使用 ```python result = skill.execute( { "input": "hello world", "options": { "format": "json", "case": "upper" } }, context ) ### 2. 使用文档 # docs/usage.md # MySkill 使用指南 ## 安装 ~~~`bash `bash pip install my-skill ## 基本使用 ### 命令行 ~~~bash bash claude --skill my-skill --input "hello world" ### Python API ```python from skills.my_skill import MySkill from claude_code_sdk import SkillContext skill = MySkill() context = SkillContext() result = skill.execute( {"input": "hello world"}, context ) print(result.data["output"]) ## 高级用法 ### 自定义选项 ```python python result = skill.execute( { "input": "hello world", "options": { "format": "yaml", "case": "title" } }, context ) ### 批量处理 ```python inputs = ["hello", "world", "test"] results = [] for input_text in inputs: result = skill.execute( {"input": input_text}, context ) results.append(result) ## 错误处理 ### 缺少参数 ```python python try: result = skill.execute({}, context) except KeyError as e: print(f"Missing parameter: {e}") ### 无效输入 ```python from skills.utils.validators import validate_parameters parameters = {"input": 123} schema = skill.get_parameters_schema() if not validate_parameters(parameters, schema): print("Invalid parameters") ### 3. 开发文档 ``` # docs/development.md # MySkill 开发指南 ## 开发环境设置 ### 安装依赖 ~~~`bash `bash pip install -e ".[dev]" ### 运行测试 ~~~bash bash pytest ### 代码格式化 ```bash black src/skills ### 代码检查 ~~~bash bash pylint src/skills ## 添加新功能 ### 1. 创建新方法 ```python def new_feature(self, data: str) -> str: """新功能""" # 实现逻辑 return data ### 2. 添加测试 ```python python def test_new_feature(self): """测试新功能""" result = self.skill.new_feature("test") assert result == "expected" ### 3. 更新文档 更新 API 文档和使用文档。 ## 发布流程 ### 1. 更新版本号 ```python # setup.py version="0.2.0" ### 2. 更新变更日志 ~~~markdown markdown # CHANGELOG.md ## [0.2.0] - 2024-01-15 ### Added - 新功能描述 ### Changed - 变更描述 ### Fixed - 修复描述 ### 3. 创建发布 ```bash git tag v0.2.0 git push origin v0.2.0 ### 4. 发布到 PyPI ~~~bash bash python setup.py sdist bdist_wheel twine upload dist/* ``` ```